<?php declare(strict_types=1);

namespace Zyucv7\Components\Http\Client;

/**
 * Curl HTTP客户端类
 * 用于发送HTTP请求并处理响应
 */
class Curl
{
    /**
     * cURL句柄
     * 
     */
    private $ch;
    
    /**
     * 请求头信息
     * 
     * @var array
     */
    private array $headers = [];
    
    /**
     * 响应内容
     * 
     * @var mixed
     */
    private $response = null;
    
    /**
     * 错误信息
     * 
     * @var string|null
     */
    private $error = null;
    
    /**
     * HTTP状态码
     * 
     * @var int|null
     */
    private $httpCode = null;

    /**
     * 是否返回JSON原始数据
     * 
     * @var bool
     */
    private $isJsonRaw = false;

    /**
     * 构造函数
     * 初始化cURL句柄并设置基本选项
     * 
     * @param string $url 请求URL
     * @param array $options 请求选项数组
     */
    public function __construct($url, array $options = [])
    {
        $this->ch = curl_init();
        curl_setopt($this->ch, CURLOPT_URL, $url);
        $this->setOptions($options);
    }

    /**
     * 构建请求头数组
     * 将关联数组格式的请求头转换为cURL所需的格式
     * 
     * @return void
     */
    private function buildHeaders()
    {
        $headerArray = [];
        foreach ($this->headers as $key => $value) {
            $headerArray[] = $key . ': ' . $value;
        }
        curl_setopt($this->ch, CURLOPT_HTTPHEADER, $headerArray);
    }

    /**
     * 执行HTTP请求
     * 发送请求并获取响应数据
     * 
     * @return void
     */
    private function execute()
    {
        $this->buildHeaders();
        $this->response = curl_exec($this->ch);
        $this->httpCode = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
        $this->error = curl_error($this->ch);
        curl_close($this->ch);
    }

    /**
     * 设置cURL选项
     * 配置请求的各种参数，如超时、SSL验证等
     * 
     * @param array $options 选项数组
     * @return void
     */
    private function setOptions(array $options = [])
    {
        // 返回响应内容而不是直接输出
        curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
        // 设置超时
        curl_setopt($this->ch, CURLOPT_TIMEOUT, $options['timeout'] ?? 30);
        // SSL验证
        $verifySsl = $options['verify_ssl'] ?? false;
        curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, $verifySsl);
        curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, $verifySsl ? 2 : 0);
        // header
        $headers = $options['headers'] ?? [];
        $this->headers = array_merge($this->headers, $headers);
        // JSON响应
        $this->isJsonRaw = $options['json_raw'] ?? false;
    }

    /**
     * 发送GET请求
     * 执行GET请求并返回当前实例以支持链式调用
     * 
     * @return self
     */
    public function sendGet()
    {
        $this->execute();
        return $this;
    }

    /**
     * 转换请求数据格式
     * 根据数据类型转换为适当的格式，如JSON或表单数据
     * 
     * @param mixed $data 请求数据
     * @return string|array 转换后的数据
     */
    private function _translateData($data)
    {
        $contentType = $this->headers['Content-Type'] ?? '';
        if($contentType === 'application/json') {
            if($this->isJsonRaw) {
                return $data;
            }
            return json_encode($data);
        }
        
        return http_build_query($data);
    }

    /**
     * 发送POST请求
     * 发送POST请求并处理请求数据
     * 
     * @param mixed $data POST数据
     * @return void
     */
    public function sendPost($data = [])
    {
        // echo $this->_translateData($data);
        curl_setopt($this->ch, CURLOPT_POST, true);
        curl_setopt($this->ch, CURLOPT_POSTFIELDS, $this->_translateData($data));
        $this->sendGet();
    }

    /**
     * 获取错误信息
     * 返回请求过程中发生的错误信息
     * 
     * @return string|null 错误信息
     */
    public function getError()
    {
        return $this->error;
    }

    /**
     * 获取HTTP状态码
     * 返回响应的HTTP状态码
     * 
     * @return int|null HTTP状态码
     */
    public function getHttpCode()
    {
        return $this->httpCode;
    }

    /**
     * 获取解析后的JSON数据
     * 将响应内容解析为关联数组
     * 
     * @return array|false 解析后的数据或false（解析失败时）
     */
    public function getData() {
        $data = json_decode($this->response, true);
        if(json_last_error() !== JSON_ERROR_NONE) {
            $this->error = json_last_error_msg();
            return false;
        }
        return $data;
    }

    /**
     * 获取原始数据
     * 返回未经处理的原始响应内容
     * 
     * @return mixed 原始响应内容
     */
    public function getRaw() {
        return $this->response;
    }
}